/*!
 * Gitee-Frontend.js v0.15.1
 * (c) 2018 Liu Chao
 * Released under the MIT License.
 */
(function () {
  'use strict';

  /* eslint-disable no-console */

  var defaults = {
    filters: [],
    groups: [],
    text: {
      loading: 'loading...',
      placeholder: 'Search or filter results...',
      searchHelp: 'Press Enter or click to search',
      selectOtherFilter: 'Select other filter',
      history: 'Histroy',
      clearHistory: 'Clear history'
    },
    removeIconClass: 'icon times',
    history: {
      limit: 5,
      store: window.localStorage,
      storeKey: 'GiteeSearchHistory'
    },
    data: function data() {
      return {};
    },
    callback: function callback(data) {
      console.log(data);
    }
  };

  function Config(config) {
    $.extend(this, defaults, config);
    if (config.text) {
      $.extend(this.text, config.text);
    }
  }

  /* eslint-disable indent */
  var template = {
    container: ['<div class="filtered-search-box-input-container">', '<ul class="tokens-container">', '<li class="input-token ui dropdown">', '<input autocomplete="off" class="search filtered-search">', '<div class="menu">', '<div class="scrolling menu filter-items"></div>', '</div>', '</li>', '</ul>', '</div>'].join(''),
    btnClearSearch: ['<button class="clear-search hidden" type="button">', '<i aria-hidden="true" data-hidden="true" class="{iconClass}"></i>', '</button>'].join(''),
    itemIcon: '<i class="item-icon{class}" style="{style}"></i>',
    itemDivider: '<div class="divider"></div>',
    itemImage: '<img class="item-image ui avatar image" alt="image" src="{content}" />',
    itemDescription: '<span class="description">{content}</span>',
    inputDropdownItem: ['<div class="item{class}" data-value="{key}">', '{icon}{name}{placeholder}', '</div>'].join(''),
    filterDropdownItem: ['<div class="item{class}" data-value="{value}">', '{icon}{image}{name}<span class="item-keywords">{keywords}</span>{description}', '</div>'].join(''),
    inputDropdownHeader: '<div class="header">{header}</div>',
    visualTokenValue: ['<div class="value-container{class}" style="{style}">', '<div class="value" data-value="{value}">{icon}{image}{name}</div>', '<div class="remove-token inverted" role="button">', '<i class="{iconClass}"></i>', '</div>', '</div>'].join(''),
    visualSearchTerm: ['<li class="js-visual-token filtered-search-term">', '<div class="name">{value}</div>', '</li>'].join(''),
    visualToken: ['<li class="js-visual-token filtered-search-token" data-key="{key}">', '<div class="selectable" role="button">', '<div class="name">{name}</div>', '{value}', '</div>', '</li>'].join('')
  };

  var htmlEscape = function () {
    var $el = $('<div/>');

    return function (text) {
      return $el.text(text).html();
    };
  }();

  function cleanCopy(obj) {
    return JSON.parse(JSON.stringify(obj));
  }

  function isEmptyFilterToken($token) {
    return $token.find('.value').length < 1;
  }

  function isSearchTermToken($token) {
    return $token.hasClass('filtered-search-term');
  }

  function findListItem(list, cmp) {
    var target = null;

    list.some(function (item) {
      if (cmp(item)) {
        target = item;
        return true;
      }
      return false;
    });
    return target;
  }

  function findFilterItemByValue(filter, value) {
    var valueStr = value.toString();
    return findListItem(filter.items, function (item) {
      return item.value.toString() === valueStr;
    });
  }

  function findFilterItemByName(filter, name) {
    if (!filter.items) {
      return null;
    }
    return findListItem(filter.items, function (item) {
      return item.name === name;
    });
  }

  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
    return typeof obj;
  } : function (obj) {
    return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  };

  var classCallCheck = function (instance, Constructor) {
    if (!(instance instanceof Constructor)) {
      throw new TypeError("Cannot call a class as a function");
    }
  };

  var createClass = function () {
    function defineProperties(target, props) {
      for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ("value" in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor);
      }
    }

    return function (Constructor, protoProps, staticProps) {
      if (protoProps) defineProperties(Constructor.prototype, protoProps);
      if (staticProps) defineProperties(Constructor, staticProps);
      return Constructor;
    };
  }();

  var inherits = function (subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
      throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
    }

    subClass.prototype = Object.create(superClass && superClass.prototype, {
      constructor: {
        value: subClass,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  };

  var possibleConstructorReturn = function (self, call) {
    if (!self) {
      throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }

    return call && (typeof call === "object" || typeof call === "function") ? call : self;
  };

  var slicedToArray = function () {
    function sliceIterator(arr, i) {
      var _arr = [];
      var _n = true;
      var _d = false;
      var _e = undefined;

      try {
        for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
          _arr.push(_s.value);

          if (i && _arr.length === i) break;
        }
      } catch (err) {
        _d = true;
        _e = err;
      } finally {
        try {
          if (!_n && _i["return"]) _i["return"]();
        } finally {
          if (_d) throw _e;
        }
      }

      return _arr;
    }

    return function (arr, i) {
      if (Array.isArray(arr)) {
        return arr;
      } else if (Symbol.iterator in Object(arr)) {
        return sliceIterator(arr, i);
      } else {
        throw new TypeError("Invalid attempt to destructure non-iterable instance");
      }
    };
  }();

  /* eslint-disable no-plusplus */

  function render(tmpl, data) {
    var html = tmpl;

    Object.keys(data).forEach(function (k) {
      html = html.replace('{' + k + '}', data[k]);
    });

    return html;
  }

  var itemDivider = template.itemDivider;

  function renderFilterDropdownItem(item) {
    var html = template.filterDropdownItem;
    var keymap = {
      image: 'itemImage',
      description: 'itemDescription'
    };

    Object.keys(keymap).forEach(function (k) {
      var node = '';

      if (item[k]) {
        node = template[keymap[k]].replace('{content}', item[k]);
      }
      html = html.replace('{' + k + '}', node);
    });

    if (item.icon) {
      html = render(html, {
        icon: render(template.itemIcon, {
          class: ' ' + item.icon,
          style: item.iconStyle || ''
        })
      });
    } else {
      html = render(html, { icon: '' });
    }
    return render(html, {
      name: htmlEscape(item.name),
      value: item.value,
      keywords: htmlEscape(item.keywords || ''),
      class: item.class ? ' ' + item.class : ''
    });
  }

  function renderInputDropdownItem(item) {
    var data = {};

    data.key = item.key || 'cmd';
    data.name = item.name;
    if (item.icon) {
      data.icon = '<i class="' + item.icon + '"></i>';
    } else {
      data.icon = '';
    }
    if (item.placeholder) {
      data.placeholder = ':' + item.placeholder;
    } else {
      data.placeholder = '';
    }
    if (item.class) {
      data.class = ' ' + item.class;
    } else {
      data.class = '';
    }
    return render(template.inputDropdownItem, data);
  }

  function renderItemIcon(data) {
    if (data.icon) {
      return render(template.itemIcon, {
        style: data.iconStyle || '',
        class: ' ' + data.icon
      });
    }
    return '';
  }

  function renderInputDropdownHeader(header) {
    return render(template.inputDropdownHeader, { header: header });
  }

  function renderVisualSearchTerm(data) {
    return render(template.visualSearchTerm, { value: htmlEscape(data.value) });
  }

  var Renderer = function () {
    function Renderer() {
      classCallCheck(this, Renderer);
    }

    createClass(Renderer, [{
      key: 'renderFilterTokenValueByItem',
      value: function renderFilterTokenValueByItem(item) {
        var html = template.visualTokenValue;
        html = render(html, {
          name: htmlEscape(item.name),
          value: item.value,
          iconClass: this.config.removeIconClass
        });
        html = render(html, { icon: renderItemIcon(item) });
        if (item.image) {
          html = render(html, {
            image: render(template.itemImage, { content: item.image })
          });
        } else {
          html = render(html, { image: '' });
        }
        if (item.color) {
          return render(html, {
            class: ' has-color',
            style: 'background-color: ' + item.color
          });
        }
        return render(html, { class: '', style: '' });
      }
    }, {
      key: 'renderFilterTokenValue',
      value: function renderFilterTokenValue(filter, value) {
        var i;
        var item;
        var items = filter.items;
        var valueStr = value.toString();

        if (filter.type === 'daterange') {
          return this.renderFilterTokenValueByItem({ name: value, value: value });
        }
        if (_typeof(filter.none) === 'object') {
          items = [filter.none].concat(items);
        }
        for (i = 0; i < items.length; ++i) {
          item = items[i];
          if (item.value.toString() === valueStr) {
            return this.renderFilterTokenValueByItem(item);
          }
        }
        return '';
      }
    }, {
      key: 'renderVisualToken',
      value: function renderVisualToken(filter, value) {
        var _this = this;

        var values;
        var valuesHtml = '';
        var html = template.visualToken;

        if (filter.multiple) {
          values = value;
        } else {
          values = [value];
        }
        html = render(template.visualToken, filter);
        if (typeof value !== 'undefined') {
          values.forEach(function (val) {
            valuesHtml += _this.renderFilterTokenValue(filter, val);
          });
        }
        html = html.replace('{value}', valuesHtml);
        return html;
      }
    }, {
      key: 'renderBody',
      value: function renderBody() {
        return [template.container, render(template.btnClearSearch, {
          iconClass: this.config.removeIconClass
        })].join('');
      }
    }]);
    return Renderer;
  }();

  /* eslint-disable no-param-reassign */

  function onIntent(e) {
    var i;
    var item;
    var that = e.data.this;
    var $target = $(e.target);
    var classes = ['.filtered-search-box'];

    that.logger.log('onIntent()');
    // 如果点击的是组件内的元素，则保持激活状态
    for (i = 0; i < classes.length; ++i) {
      if ($target.parents(classes[i]).length > 0) {
        that.lockShow();
        that.focus();
        return;
      }
    }
    if (!that.target) {
      return;
    }
    that.active = false;
    // 如果输入框后面没有筛选器，就不移动输入框了
    if (that.$inputDropdown.next('.js-visual-token').length < 1) {
      return;
    }
    item = findFilterItemByName(that.target, that.targetValue);
    if (item) {
      that.selectFilterValue(item.value);
    }
    that.$input.val('');
    that.clearTargetFilter();
    that.resetInputPosition();
  }

  // 初始化筛选器的事件处理
  function initTokenEventHandler(that) {
    that.$el.on('click', '.js-visual-token', function (e) {
      var $value;
      var $target = $(e.target);
      var $token = $target.parents('.js-visual-token');

      e.stopPropagation();
      // 如果点击的是删除按钮
      if ($target.hasClass('remove-token') || $target.parent().hasClass('remove-token')) {
        $value = $target.parents('.value-container').find('.value');
        that.removeFilterValue($token, $value.data('value'));
        // 如果当前输入框已经获得焦点，则让它保持焦点
        if (that.hasFocus()) {
          that.focus();
          that.deferFocusTime();
        }
        return;
      }
      that.editToken($token);
    });
  }

  // 初始化输入框内的输入事件捕获，用于处理用户手动输入的筛选条件
  function initInputTokenCatch(that) {
    var keyHandlers = {
      'Backspace': function Backspace(value) {
        var $token;

        // 只在输入框内容为空时处理条件删除操作
        if (value) {
          return;
        }
        if (that.target) {
          $token = that.$target;
          // 如果该筛选标记元素没有值，则切换编辑筛选器名称
          if (isEmptyFilterToken($token)) {
            value = that.target.name;
            that.clearTargetFilter();
            that.removeFilterToken($token);
          } else {
            that.popCurrentFilterValue();
            that.buildInputDropdown();
          }
          return;
        }
        // 取上一个筛选器，然后删除它的最后一个选项
        $token = that.$inputDropdown.prev('.js-visual-token');
        if ($token.length > 0) {
          if (isSearchTermToken($token)) {
            that.editTerm();
            return;
          }
          that.popFilterValue($token);
          if (isEmptyFilterToken($token)) {
            $token.remove();
          }
          that.buildInputDropdown();
        }
      },
      'Enter': function Enter() {
        if (!that.target) {
          that.submit();
          that.buildInputDropdown();
          setTimeout(function () {
            that.$input.focus();
          }, 10);
        }
      },
      ':': function _(value) {
        var filter;

        if (that.target) {
          that.$input.val(value);
          return;
        }
        filter = that.getFilterByName(value);
        if (filter && that.newFilter(filter.key)) {
          that.$input.val('');
          return;
        }
        that.$input.val(value);
      },
      // 筛选选项分割符，针对可多选的筛选器
      ',': function _(value) {
        var item;

        if (!that.target) {
          return;
        }
        item = findFilterItemByName(that.target, value);
        if (item && that.selectFilterValue(item.value)) {
          that.$input.val('');
          that.input = '';
          that.buildFilterDropdown(that.target);
        } else {
          that.$input.val(value);
        }
      },
      // 筛选结束符
      ';': function _(value) {
        var item;

        if (!that.target) {
          that.$input.val(value);
          return;
        }
        item = findFilterItemByName(that.target, value);
        if (item && that.selectFilterValue(item.value)) {
          that.$input.val('');
          that.input = '';
          that.clearTargetFilter();
        } else {
          that.$input.val(value);
        }
      }
    };

    that.$input.on({
      focus: function focus() {
        that.$el.addClass('focus');
      },
      blur: function blur() {
        that.$el.removeClass('focus'); /**
                                       that.active = false
                                       that.deferFocusTime() */
      },
      input: function input() {
        that.input = that.$input.val();
        that.updateState();
      }
    });
    that.$el.on('click', function () {
      that.focus();
    });
    that.$input.on('keydown', function (e) {
      var val = that.$input.val();

      if (!keyHandlers[e.key]) {
        return;
      }
      // 延迟一会，避免在给输入框赋值后又被后面的 inout 事件重置
      setTimeout(function () {
        keyHandlers[e.key](val);
      }, 10);
    });
  }

  // 初始化“清除”按钮
  function initClearButton(that) {
    that.$btnClearSearch.on('click', function () {
      that.clear();
      that.commit();
    });
  }

  function bindIntent(target) {
    $(document).on('mousedown', { this: target }, onIntent);
  }

  function unbindIntent() {
    $(document).off('mousedown', onIntent);
  }

  // 阻止已冒泡到 document 的 click 事件的处理
  function stopIntent(that) {
    unbindIntent();
    setTimeout(function () {
      bindIntent(that);
    }, 100);
  }

  function bindEventHandlers(target) {
    initTokenEventHandler(target);
    initInputTokenCatch(target);
    initClearButton(target);
  }

  function unbindEventHandlers() {
    unbindIntent();
  }

  var Builder = function (_Renderer) {
    inherits(Builder, _Renderer);

    function Builder() {
      classCallCheck(this, Builder);
      return possibleConstructorReturn(this, (Builder.__proto__ || Object.getPrototypeOf(Builder)).apply(this, arguments));
    }

    createClass(Builder, [{
      key: 'buildFilterDropdownItems',

      /**
       * 构建筛选器的下拉菜单选项
       * @param {Object} filter 筛选器对象
       */
      value: function buildFilterDropdownItems(filter) {
        var that = this;
        var item = {};

        that.$inputDropdownMenu.empty();
        // 如果条件支持多选，则添加一条选项，让用户点击退出此条件的选择
        if (filter.multiple) {
          that.$inputDropdownMenu.append(renderInputDropdownItem({
            key: '<cmd:back>',
            icon: 'icon angle double left',
            class: 'item-fixed',
            name: that.text.selectOtherFilter
          }), itemDivider);
        }
        // 如果有设置“无“选项
        if (_typeof(filter.none) === 'object') {
          Object.keys(filter.none).forEach(function (k) {
            item[k] = filter.none[k];
          });
          if (item.class) {
            item.class = item.class + ' item-fixed';
          } else {
            item.class = 'item-fixed';
          }
          that.$inputDropdownMenu.append(renderFilterDropdownItem(item), itemDivider);
        }
        this.getFilterAvailableItems(filter).forEach(function (filterItem) {
          that.$inputDropdownMenu.append(renderFilterDropdownItem(filterItem));
        });
        that.$inputDropdown.dropdown('refresh');
      }
    }, {
      key: 'buildFilterDropdownRemoteItems',
      value: function buildFilterDropdownRemoteItems(filter) {
        var that = this;
        that.$inputDropdownMenu.empty().append(renderInputDropdownItem({
          key: '<cmd:wait>',
          icon: 'ui active inline mini loader',
          class: 'item-fixed',
          name: that.text.loading
        }));
        that.loadFilterRemoteItems(filter, function () {
          if (that.target && that.target.key === filter.key) {
            that.buildFilterDropdownItems(filter);
          }
        });
      }

      /**
       * 构建时间范围选择器
       * @param {Object} filter 筛选器对象
       */

    }, {
      key: 'buildDateRangePicker',
      value: function buildDateRangePicker(filter) {
        var picker;
        var that = this;
        var value = this.targetValue;
        var values;

        that.$inputDropdown.dropdown({
          on: 'custom',
          onShow: function onShow() {
            return false;
          }
        }).dropdown('hide');
        that.removeDateRangePicker();
        that.$input.daterangepicker(filter.config);
        picker = that.$input.data('daterangepicker');
        if (value) {
          values = value.split(picker.locale.separator);
          picker.setStartDate(values[0]);
          picker.setEndDate(values[1]);
        }
        that.$input.on({
          'apply.daterangepicker': function applyDaterangepicker() {
            var start = picker.startDate.format(picker.locale.format);
            var end = picker.endDate.format(picker.locale.format);
            that.selectFilterValue(start + picker.locale.separator + end);
            that.clearTargetFilter();
            that.$input.val('');
            picker.remove();
          },
          'cancel.daterangepicker': function cancelDaterangepicker() {
            that.selectFilterValue(value);
            that.clearTargetFilter();
            that.$input.val('');
            picker.remove();
          }
        });
        picker.show();
      }

      /**
       * 构建筛选器的下拉框，列出当前筛选器的可选选项
       * @param {Object} filter 筛选器对象
       */

    }, {
      key: 'buildFilterDropdown',
      value: function buildFilterDropdown(filter) {
        var that = this;

        that.updatePlaceholder();
        if (filter.type === 'daterange') {
          this.buildDateRangePicker(filter);
          return;
        }
        if (_typeof(filter.remote) === 'object') {
          that.buildFilterDropdownRemoteItems(filter);
        } else {
          that.buildFilterDropdownItems(filter);
        }
        that.$inputDropdown.removeClass('input-filter-token').addClass('input-filter-value-token').dropdown({
          forceSelection: false,
          fullTextSearch: true,
          selectOnKeydown: false,
          onHide: function onHide() {
            that.logger.log('buildFilterDropdown() hide?', !that.hasFocus());
            // 根据当前状态确定是否阻止隐藏下拉框
            return !that.hasFocus();
          },
          action: function action(_text, value) {
            var item;

            that.lockShow();

            if (!value) {
              return true;
            }
            if (that.execCommand(value)) {
              return true;
            }
            that.input = '';
            that.targetValue = '';
            if (_typeof(filter.none) === 'object' && filter.none.value.toString() === value) {
              that.clearFilterValue();
              that.selectFilterValue(value);
              that.clearTargetFilter();
              return false;
            }
            item = that.getCurrentFilterItem(value);
            if (item) {
              that.selectFilterValue(value);
            }
            // 如果是多选，则重置下拉框
            if (filter.multiple) {
              // 没有可选选项就退出编辑筛选器
              if (!that.visibleFilter(filter)) {
                that.clearTargetFilter();
              } else {
                that.buildFilterDropdown(filter);
                that.$input.val('');
                that.$input.focus();
              }
            } else {
              that.clearTargetFilter();
            }
            stopIntent(that);
            return false;
          },
          onChange: function onChange(value) {
            var item;

            that.lockShow();
            if (value && value.indexOf('<cmd:') === 0) {
              return;
            }
            item = that.getCurrentFilterItem(value);
            if (item) {
              that.$input.val(item.name);
            }
          }
        }).dropdown('clear');

        this.logger.log('[' + filter.key + ']', 'buildFilterDropdown()');
      }

      /**
       * 构建已分组的下拉菜单选项
       * @param {Array} 分组参数列表
       * @returns {Array} 已被分组的筛选器的 key 列表
       */

    }, {
      key: 'buildGroupedDropdownItems',
      value: function buildGroupedDropdownItems(groups) {
        var that = this;
        var groupedFilters = [];

        groups.forEach(function (group) {
          var html = '';

          that.filters.forEach(function (filter) {
            if (group.keys.indexOf(filter.key) < 0) {
              return;
            }
            groupedFilters.push(filter.key);
            if (!that.visibleFilter(filter)) {
              return;
            }
            html += renderInputDropdownItem(filter);
          });
          if (html) {
            html = renderInputDropdownHeader(group.name) + html;
          }
          that.$inputDropdownMenu.append(html);
        });
        return groupedFilters;
      }
    }, {
      key: 'buildHistoryItems',
      value: function buildHistoryItems() {
        if (this.config.history && this.history.load().length > 0) {
          this.$inputDropdownMenu.append(renderInputDropdownHeader(this.text.history), this.history.render(), renderInputDropdownItem({
            key: '<cmd:clear-history>',
            name: this.text.clearHistory
          }), itemDivider);
        }
      }
    }, {
      key: 'clearSearchItem',
      value: function clearSearchItem() {
        this.$input.off('input.dropdown');
      }

      // 构建“搜索”选项

    }, {
      key: 'buildSearchItem',
      value: function buildSearchItem() {
        var that = this;
        var $cmd = $(renderInputDropdownItem({
          key: '<cmd:search>',
          icon: 'icon search',
          class: 'item-fixed',
          name: that.text.searchHelp
        }));

        that.$inputDropdownMenu.append($cmd);
        // 在输入内容时，更新“搜索”选项的值，让下拉框能将它作为第一个匹配的选项，并默认选中它
        that.$input.on('input.dropdown', function () {
          // 将输入框当前值放在前面，确保下拉框能够搜索匹配到
          $cmd.attr('data-value', that.$input.val() + ';<cmd:search>');
          that.$inputDropdown.dropdown('refresh');
        });
      }
    }, {
      key: 'buildInputDropdown',
      value: function buildInputDropdown() {
        var html = '';
        var that = this;
        var groupedFilters = [];

        this.clearSearchItem();
        if (that.target) {
          that.buildFilterDropdown(that.target);
          return;
        }
        that.updatePlaceholder();
        that.logger.log('buildInputDropdown()');
        that.$inputDropdownMenu.empty();
        that.buildSearchItem();
        that.buildHistoryItems();
        if (that.groups.length > 0) {
          groupedFilters = that.buildGroupedDropdownItems(that.groups);
        }
        that.filters.forEach(function (filter) {
          if (groupedFilters.indexOf(filter.key) >= 0) {
            return;
          }
          if (!that.visibleFilter(filter)) {
            return;
          }
          html += renderInputDropdownItem(filter);
        });
        if (html && groupedFilters.length > 0) {
          html = itemDivider + html;
        }
        that.$inputDropdownMenu.append(html);
        that.$inputDropdown.addClass('input-filter-token').removeClass('input-filter-value-token').dropdown({
          forceSelection: false,
          selectOnKeydown: false,
          onHide: function onHide() {
            if (that.input) {
              that.applyInput();
            }
            if (!that.hasFocus()) {
              that.resetInputPosition();
            }
            that.logger.log('buildInputDropdown() hide?', !that.hasFocus());
            return !that.hasFocus();
          },
          action: function action(_text, value) {
            that.logger.log('action():', value);
            that.lockShow();

            if (value) {
              if (that.newFilter(value)) {
                return;
              }
            }
            if (that.input) {
              that.applyInput();
              that.buildInputDropdown();
              that.execCommand(value);
            } else if (that.execCommand(value)) {
              // 清空下拉框的值，以取消选项的“已选中”的效果
              that.$inputDropdown.dropdown('clear');
              that.logger.log('execCommand');
              that.applyInput();
            }
          }
        }).dropdown('clear');
      }

      // 构建已选中的筛选器标记

    }, {
      key: 'buildFilterTokens',
      value: function buildFilterTokens() {
        var that = this;

        that.loadingFilters = 0;
        that.filters.forEach(function (filter) {
          var html;
          var value = that.data[filter.key];

          that.logger.log('buildFilterTokens():', filter.key);
          if (typeof value === 'undefined') {
            return;
          }
          if (filter.multiple) {
            if (value.length < 1) {
              return;
            }
          } else if (value === '') {
            return;
          }
          if (that.checkFilterItemsLoaded(filter)) {
            html = that.renderVisualToken(filter, value);
            $(html).insertBefore(that.$inputDropdown);
            return;
          }
          that.setDisabled(true);
          that.loadingFilters += 1;
          that.loadFilterRemoteItems(filter, function () {
            var selector = '.js-visual-token[data-key="' + filter.key + '"]';

            that.$el.find(selector).remove();
            html = that.renderVisualToken(filter, that.data[filter.key]);
            $(html).insertBefore(that.$inputDropdown);
            that.loadingFilters -= 1;
            if (that.loadingFilters < 1) {
              that.setDisabled(false);
              that.buildSearchTerm();
            }
          });
        });
      }

      // 更新搜索条件的内容及位置

    }, {
      key: 'buildSearchTerm',
      value: function buildSearchTerm() {
        if (this.$search && this.$search.length > 0) {
          if (this.data.search) {
            this.$search.find('.name').text(this.data.search);
          } else {
            this.$search.remove();
            this.$search = null;
            return;
          }
        } else if (this.data.search) {
          this.$search = $(renderVisualSearchTerm({ value: this.data.search }));
        } else {
          return;
        }
        // 移动元素时会触发下拉框的 hide 行为，而 onHide() 回调里有调用 applyInput() 方法，
        // 等于递归调用了一次，为避免重复记录搜索词，在这里加个延迟
        setTimeout(function () {
          // 顺带更新条件和输入框的位置
          this.deferFocusTime();
          this.$tokens.append(this.$search, this.$inputDropdown);
        }.bind(this));
      }

      // 构建组件基础内容和功能

    }, {
      key: 'build',
      value: function build() {
        this.$el.html(this.renderBody());
        this.$el.addClass('filtered-search-box');
        this.$tokens = this.$el.find('.tokens-container');
        this.$input = this.$el.find('.filtered-search');
        this.$inputDropdown = this.$input.parent();
        this.$inputDropdownMenu = this.$inputDropdown.find('.filter-items');
        this.$btnClearSearch = this.$el.find('.clear-search');
        this.buildInputDropdown();
        this.buildFilterTokens();
        this.buildSearchTerm();
        bindIntent(this);
      }
    }]);
    return Builder;
  }(Renderer);

  /* eslint-disable no-plusplus */

  function renderHistroyItem(item) {
    return renderInputDropdownItem({
      name: item.name,
      class: 'history-item',
      key: '<cmd:apply:' + escape(JSON.stringify(item.value)) + '>'
    });
  }

  var History = function () {
    function History(config) {
      classCallCheck(this, History);

      if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') {
        $.extend(this, defaults.history, config);
      } else {
        $.extend(this, defaults.history);
      }
    }

    createClass(History, [{
      key: 'load',
      value: function load() {
        try {
          var data = JSON.parse(this.store.getItem(this.storeKey));
          if (data instanceof Array) {
            return data;
          }
        } catch (e) {
          return [];
        }
        return [];
      }

      /**
       * 添加历史记录
       * @param {Array} filters 筛选器列表，用于为条件组生成名称
       */

    }, {
      key: 'add',
      value: function add(filters, data) {
        var name = [];
        var history = this.load();

        // 生成条件组名称
        filters.forEach(function (filter) {
          var value = data[filter.key];

          if (!value) {
            return;
          }
          if (filter.none && value.toString() === filter.none.value.toString()) {
            name.push(filter.name + ': ' + filter.none.name);
            return;
          }
          if (filter.multiple && value instanceof Array) {
            var values = [];

            value.forEach(function (v) {
              var item = findFilterItemByValue(filter, v);
              if (item) {
                values.push(item.name);
              }
            });
            if (values.length > 0) {
              name.push(filter.name + ': ' + values.join(', '));
            }
            return;
          }
          if (filter.type === 'daterange') {
            name.push(filter.name + ': ' + value);
            return;
          }

          var item = findFilterItemByValue(filter, value);

          if (item) {
            name.push(filter.name + ': ' + item.name);
          }
        });

        if (data.search) {
          name.push(data.search);
        }
        if (name.length < 1) {
          return false;
        }
        name = name.join('; ');
        // 删除同名的历史记录
        for (var i = 0; i < history.length; ++i) {
          if (history[i].name === name) {
            history.splice(i, 1);
            break;
          }
        }
        history.splice(0, 0, { name: name, value: data });
        history = history.splice(0, this.limit);
        this.store.setItem(this.storeKey, JSON.stringify(history));
        return true;
      }
    }, {
      key: 'clear',
      value: function clear() {
        this.store.removeItem(this.storeKey);
      }
    }, {
      key: 'render',
      value: function render() {
        var html = [];
        var history = this.load();

        history.forEach(function (item) {
          html.push(renderHistroyItem(item));
        });
        return html.join('');
      }
    }]);
    return History;
  }();

  var handlers = {
    search: function search() {
      this.submit();
    },
    back: function back() {
      var item = null;

      if (this.targetValue) {
        item = findFilterItemByName(this.target, this.targetValue);
      }

      this.input = '';
      this.targetValue = '';

      if (item) {
        this.selectFilterValue(item.value);
        this.clearTargetFilter();
        // 移动输入框到筛选器列表的末尾
        this.$tokens.append(this.$inputDropdown);
      } else {
        this.clearTargetFilter();
      }
      this.$input.focus();
    },
    apply: function apply(paramsStr) {
      var params = JSON.parse(unescape(paramsStr));
      this.active = true;
      this.setData(params);
      this.commit();
    },

    'clear-history': function clearHistory() {
      this.history.clear();
      this.buildInputDropdown();
    }
  };

  function exec(_this, cmd) {
    var i = cmd.indexOf('<cmd:');

    if (i < 0) {
      return false;
    }

    var _cmd$slice$split = cmd.slice(i + 5, -1).split(':'),
        _cmd$slice$split2 = slicedToArray(_cmd$slice$split, 2),
        name = _cmd$slice$split2[0],
        params = _cmd$slice$split2[1];

    if (handlers[name]) {
      handlers[name].call(_this, params);
      return true;
    }
    return false;
  }

  /* eslint-disable no-extra-semi */

  var FilteredSearchBox = function (_Builder) {
    inherits(FilteredSearchBox, _Builder);

    function FilteredSearchBox($el, options) {
      classCallCheck(this, FilteredSearchBox);

      var config = new Config(options);

      var _this = possibleConstructorReturn(this, (FilteredSearchBox.__proto__ || Object.getPrototypeOf(FilteredSearchBox)).call(this));

      _this.config = config;
      _this.text = config.text;
      _this.data = config.data();
      _this.groups = config.groups;
      _this.filters = config.filters;
      _this.callback = config.callback;

      _this.input = '';
      _this.store = {};
      _this.active = false;
      _this.target = null;
      _this.targetValue = null;
      _this.history = new History(config.history);

      _this.$el = $el;
      _this.$search = null;
      _this.$tokens = null;
      _this.$target = null;
      _this.$targetValues = null;

      _this.logger = {
        log: function log() {}
      };
      if (options.debug) {
        _this.logger = console;
      }
      if (typeof _this.data.search !== 'string') {
        _this.data.search = '';
      }

      _this.build();
      bindEventHandlers(_this);
      return _this;
    }

    // 清空选择器的选项


    createClass(FilteredSearchBox, [{
      key: 'clearFilterValue',
      value: function clearFilterValue() {
        var filter = this.target;
        var $token = this.$target;
        var values = this.data[filter.key];
        var $values = this.$targetValues.find('.value-container');

        if (filter.multiple && values) {
          $token.find('.value').each(function () {
            var i;
            var value = $(this).data('value');
            if (typeof value === 'undefined') {
              return;
            }
            value = value.toString();
            for (i = 0; i < values.length; ++i) {
              if (values[i].toString() === value) {
                values.splice(i, 1);
                break;
              }
            }
          });
          if (values.length < 1) {
            delete this.data[filter.key];
          }
        } else {
          delete this.data[filter.key];
        }
        $values.remove();
        this.commit();
        this.buildFilterDropdown(filter);
      }

      /**
       * 清除筛选器依赖表里的筛选器
       * 一个筛选器被其它筛选器依赖后，当这个筛选器的值被改变时，其它依赖筛选器也需要清除
       * @param {Object} filterKey 筛选器标识
       * @returns {Number} 清除的筛选器的数量
       */

    }, {
      key: 'clearRequiredFilters',
      value: function clearRequiredFilters(filterKey) {
        var that = this;
        var count = 0;

        that.filters.forEach(function (f) {
          if (f.requires instanceof Array) {
            if (f.requires.indexOf(filterKey) >= 0) {
              that.removeFilter(f.key);
              count += 1;
            }
          }
        });
        return count;
      }

      /**
       * 为筛选选器选择一个选项
       * @param {String} value 选项值
       * @returns {Boolean} 是否成功选中
       */

    }, {
      key: 'selectFilterValue',
      value: function selectFilterValue(value) {
        var html;
        var filter = this.target;
        var $values = this.$targetValues;
        var values = this.data[filter.key];

        if (!$values) {
          return false;
        }
        if (filter.multiple) {
          if (values instanceof Array && values.indexOf(value.toString()) >= 0) {
            return false;
          }
        } else if (values === value.toString()) {
          return false;
        }
        html = this.renderFilterTokenValue(filter, value);
        if (html) {
          $values.append(html);
          if (filter.multiple) {
            if (this.data[filter.key] instanceof Array) {
              this.data[filter.key].push(value);
            } else {
              this.data[filter.key] = [value];
            }
          } else {
            this.data[filter.key] = value;
          }
          this.clearRequiredFilters(filter.key);
          this.commit();
          return true;
        }
        return false;
      }

      /**
       * 移除当前筛选器中的一个选项标记
       * @param {jQuery} $token 筛选器标记
       * @param {String} value 筛选选项值
       * @returns {Boolean} 是否移除成功
       */

    }, {
      key: 'removeFilterValue',
      value: function removeFilterValue($token, value) {
        var values;
        var filter;

        filter = this.getFilterByKey($token.data('key'));
        if (!filter) {
          return false;
        }
        $token.find('.value[data-value="' + value + '"]').parent().remove();
        values = this.data[filter.key];
        if (filter.multiple) {
          value = value.toString();
          values.some(function (v, i) {
            if (v.toString() === value) {
              values.splice(i, 1);
              return true;
            }
            return false;
          });
        } else {
          delete this.data[filter.key];
        }
        if (isEmptyFilterToken($token)) {
          // 如果不是当前正编辑的筛选条件，则移除筛选器并取消编辑对象
          if (!this.target || this.target.key !== filter.key) {
            this.removeFilterToken($token);
            this.clearTargetFilter();
          }
        }
        this.clearRequiredFilters(filter.key);
        this.buildInputDropdown();
        this.commit();
        return true;
      }
    }, {
      key: 'doRemoveFilter',
      value: function doRemoveFilter(filterKey) {
        var $token;

        delete this.data[filterKey];
        $token = this.$el.find('.js-visual-token[data-key="' + filterKey + '"]');
        $token.remove();
      }
    }, {
      key: 'removeFilter',
      value: function removeFilter(filterKey) {
        this.doRemoveFilter(filterKey);
        this.clearRequiredFilters(filterKey);
        this.buildInputDropdown();
        this.commit();
      }

      /**
       * 移除一个筛选器标记元素
       * @param {jQuery} $token 筛选器标记元素
       */

    }, {
      key: 'removeFilterToken',
      value: function removeFilterToken($token) {
        var key = $token.data('key');
        var values = this.data[key];
        var filter = this.getFilterByKey(key);

        if (!filter) {
          return false;
        }
        if (filter.type === 'daterange') {
          delete this.data[key];
        } else if (filter.multiple && values) {
          $token.find('.value').each(function () {
            var i;
            var value = $(this).data('value');
            if (typeof value === 'undefined') {
              return;
            }
            value = value.toString();
            for (i = 0; i < values.length; ++i) {
              if (values[i].toString() === value) {
                values.splice(i, 1);
                break;
              }
            }
          });
          if (values.length < 1) {
            delete this.data[key];
          }
        } else {
          delete this.data[key];
        }
        $token.remove();
        this.clearRequiredFilters(key);
        this.buildInputDropdown();
        this.commit();
        return true;
      }
    }, {
      key: 'newFilter',
      value: function newFilter(filterKey) {
        var $token;
        var filter = null;

        this.logger.log('newFilter()', filterKey);
        filter = this.getFilterByKey(filterKey);
        if (!filter || !this.visibleFilter(filter)) {
          return false;
        }
        $token = this.$el.find('[data-key="' + filterKey + '"]');
        if ($token.length < 1) {
          $token = $(this.renderVisualToken(filter));
          $token.insertBefore(this.$inputDropdown);
        }
        this.selectFilterToken($token);
        this.updateState();
        return true;
      }

      // 编辑搜索条件内容

    }, {
      key: 'editTerm',
      value: function editTerm() {
        if (this.$search) {
          this.$search.remove();
        }
        this.$search = null;
        this.buildInputDropdown();
        this.input = this.data.search;
        this.data.search = '';
        this.$input.val(this.input);
        this.$input.focus();
      }

      /**
       * 编辑筛选器
       * @param {jQuery} $token 目标筛选器的标记元素
       * @returns {Boolean} 是否成功切换编辑状态
       */

    }, {
      key: 'editFilter',
      value: function editFilter($token) {
        var filter = this.getFilterByKey($token.data('key'));
        if (this.target && this.target.key === filter.key) {
          this.$input.focus();
          return true;
        }
        // 如果当前有正编辑的筛选器，则保存它
        if (this.targetValue) {
          var item = findFilterItemByName(this.target, this.targetValue);
          if (item) {
            this.selectFilterValue(item.value);
          }
        }
        this.logger.log('editFilter()');
        if (this.selectFilterToken($token)) {
          this.editCurrentFilterValue();
          return true;
        }
        return false;
      }
    }, {
      key: 'editToken',
      value: function editToken($token) {
        if (isSearchTermToken($token)) {
          this.editTerm();
        } else {
          this.editFilter($token);
        }
      }

      /**
       * 获取筛选器标记元素
       * @param {Object} filter 筛选器对象
       * @param {jQuery} $anchor 作为锚点的标记元素（可选），如果指定该参数，则会以它为起点，
       * 在它前面的元素中查找匹配的筛选器标记元素。
       */

    }, {
      key: 'getFilterToken',
      value: function getFilterToken(filter, $anchor) {
        var $token;
        var selector = '.js-visual-token[data-key="' + filter.key + '"]';

        if ($anchor) {
          $token = $anchor.prev(selector);
        } else {
          $token = this.$el.find(selector);
        }
        if ($token.length < 1) {
          return null;
        }
        return $token.last();
      }

      /**
       * 取出指定筛选器的一个选项
       * @param {jQuery} $token 筛选器标记元素
       * @returns {String} 取出的选项内容，如果没有则返回 null
       */

    }, {
      key: 'popFilterValue',
      value: function popFilterValue($token) {
        var value;
        var item;
        var filter = this.getFilterByKey($token.data('key'));

        if (!filter) {
          return null;
        }
        if (filter.type === 'daterange') {
          $token.find('.value-container').remove();
          value = this.data[filter.key];
          delete this.data[filter.key];
        } else {
          if (filter.multiple) {
            value = this.data[filter.key].pop();
            $token.find('.value[data-value="' + value + '"]').parent().remove();
          } else {
            $token.find('.value-container').remove();
            value = this.data[filter.key];
            delete this.data[filter.key];
          }
          item = findFilterItemByValue(filter, value);
          if (item) {
            value = item.name;
          } else if (_typeof(filter.none) === 'object') {
            if (filter.none.value.toString() === value.toString()) {
              value = filter.none.name;
            }
          }
        }
        this.logger.log('[' + filter.key + '] pop value:', value);
        return value;
      }

      /**
       * 取出当前选中筛选器的一个选项
       * @returns {String} 取出的选项内容，如果没有则返回 null
       */

    }, {
      key: 'popCurrentFilterValue',
      value: function popCurrentFilterValue() {
        return this.popFilterValue(this.$target);
      }

      /**
       * 取出当前选中筛选器的一个选项进行编辑
       * @returns {String} 取出的选项内容，如果没有则返回 null
       */

    }, {
      key: 'editCurrentFilterValue',
      value: function editCurrentFilterValue() {
        var value = this.popCurrentFilterValue();

        if (!value) {
          return null;
        }
        this.targetValue = value;
        // 将输入框移动到目标筛选条件后面
        this.$inputDropdown.insertAfter(this.$target);
        this.buildInputDropdown();
        this.$input.val(value).focus();
        return value;
      }

      /**
       * 选中一个筛选器，作为当前编辑对象
       * @param {jQuery} $token 筛选器标记元素
       * @return {Boolean} 是否选中成功
       */

    }, {
      key: 'selectFilterToken',
      value: function selectFilterToken($token) {
        var filter = this.getFilterByKey($token.data('key'));

        if (!filter) {
          return false;
        }
        if (this.target) {
          if (this.target.key === filter.key) {
            return true;
          }
        }

        this.input = '';
        this.target = filter;
        this.$target = $token;
        this.$targetValues = $token.find('.selectable');
        // 将输入框定位到筛选器标记元素的后面
        this.$inputDropdown.insertAfter($token);
        this.buildFilterDropdown(this.target);
        this.$input.focus();
        return true;
      }
    }, {
      key: 'clearTargetFilter',
      value: function clearTargetFilter() {
        var that = this;
        var picker;

        if (that.target) {
          if (isEmptyFilterToken(that.$target)) {
            that.$target.remove();
          }
          if (that.target.type === 'daterange') {
            picker = that.$input.data('daterangepicker');
            if (picker) {
              picker.remove();
            }
          }
          that.applyInput();
        }

        that.target = null;
        that.targetValue = '';
        that.$target = null;
        that.$targetValues = null;
        that.buildInputDropdown();
      }

      /**
       * 从远程加载筛选器选项列表
       * @param {Object} filter 筛选器对象
       * @param {function} callback 加载完后的回调
       */

    }, {
      key: 'loadFilterRemoteItems',
      value: function loadFilterRemoteItems(filter, callback) {
        var store;
        var cache;
        var that = this;
        var key = filter.key;
        var options = {
          url: filter.remote.url
        };
        var fetcher = function fetcher(ajaxOptions, onSuccess, onError) {
          return $.ajax(ajaxOptions).done(onSuccess).fail(onError);
        };

        if (typeof filter.remote.params === 'function') {
          options.data = filter.remote.params(this.data);
          key = JSON.stringify(options.data);
        } else if (filter.remote.params) {
          options.data = filter.remote.params;
          key = JSON.stringify(options.data);
        }
        store = that.store[filter.key];
        if ((typeof store === 'undefined' ? 'undefined' : _typeof(store)) !== 'object') {
          store = {
            cache: {}
          };
          this.store[filter.key] = store;
        }
        store.key = key;
        cache = store.cache[key];
        if ((typeof cache === 'undefined' ? 'undefined' : _typeof(cache)) !== 'object') {
          cache = {
            loading: true,
            items: []
          };
          store.cache[key] = cache;
        }
        if (!cache.loading) {
          filter.items = cache.items;
          that.logger.log('[' + filter.key + '] use cached items');
          if (callback) {
            callback();
          }
          return;
        }
        that.logger.log('[' + filter.key + '] load items from remote:', options.url);
        if (typeof filter.remote.fetcher === 'function') {
          fetcher = filter.remote.fetcher;
        }
        fetcher(options, function (data) {
          if (typeof filter.remote.converter === 'function') {
            cache.items = filter.remote.converter(data);
          } else {
            cache.items = data;
          }
          cache.loading = false;
          filter.items = cache.items;
          if (callback) {
            callback();
          }
        }, function () {
          store.loading = false;
        });
      }
    }, {
      key: 'getCurrentFilterItem',
      value: function getCurrentFilterItem(value) {
        if (this.target && this.checkFilterItemsLoaded(this.target)) {
          return findFilterItemByValue(this.target, value);
        }
        return false;
      }
    }, {
      key: 'execCommand',
      value: function execCommand(cmd) {
        return exec(this, cmd);
      }
    }, {
      key: 'removeDateRangePicker',
      value: function removeDateRangePicker() {
        var picker = this.$input.data('daterangepicker');

        if (picker) {
          picker.remove();
        }
        this.$input.off('apply.daterangepicker cancel.daterangepicker');
      }

      // 应用当前输入的内容，更新元素

    }, {
      key: 'applyInput',
      value: function applyInput() {
        this.logger.log('applyInput()', this.input);
        if (this.data.search) {
          this.data.search += this.input;
        } else {
          this.data.search = this.input;
        }
        // 没有目标筛选器的话，就当成更新搜索条件
        if (!this.target) {
          this.buildSearchTerm();
        }
        this.$input.val('');
        this.input = '';
      }

      /**
       * 设置是否禁用此组件
       * @param {Boolean} disabled 是否禁用
       */

    }, {
      key: 'setDisabled',
      value: function setDisabled(disabled) {
        this.$input.prop('disabled', disabled);
        if (disabled) {
          this.active = false;
          this.$el.addClass('disabled');
          this.$inputDropdown.addClass('disabled').dropdown('hide');
        } else {
          this.$el.removeClass('disabled');
          this.$inputDropdown.removeClass('disabled');
        }
      }
    }, {
      key: 'getFilterByName',
      value: function getFilterByName(name) {
        return findListItem(this.filters, function (f) {
          return f.name === name;
        });
      }
    }, {
      key: 'getFilterByKey',
      value: function getFilterByKey(key) {
        return findListItem(this.filters, function (f) {
          return f.key === key;
        });
      }

      /**
       * 获取筛选器可用的选项列表
       * @param {Object} filter 筛选器对象
       */

    }, {
      key: 'getFilterAvailableItems',
      value: function getFilterAvailableItems(filter) {
        var that = this;
        var selectedItems = that.data[filter.key];

        if (typeof selectedItems === 'undefined' || selectedItems.length < 1) {
          return filter.items;
        }
        // 如果是单选的筛选条件，只要选择一个选项，就不允许继续选了
        if (!filter.multiple) {
          return [];
        }
        if (_typeof(filter.none) === 'object') {
          if (selectedItems[0].toString() === filter.none.value.toString()) {
            return [];
          }
        }
        return filter.items.filter(function (item) {
          return !selectedItems.some(function (selected) {
            return selected.toString() === item.value.toString();
          });
        });
      }

      /**
       * 检查筛选器选项列表是否已经加载完毕
       * @param {Object} filter 筛选器对象
       */

    }, {
      key: 'checkFilterItemsLoaded',
      value: function checkFilterItemsLoaded(filter) {
        var cache;
        var store = this.store[filter.key];

        if (_typeof(filter.remote) !== 'object') {
          return true;
        }
        if ((typeof store === 'undefined' ? 'undefined' : _typeof(store)) === 'object' && store.key) {
          cache = store.cache[store.key];
          if ((typeof cache === 'undefined' ? 'undefined' : _typeof(cache)) === 'object' && !cache.loading) {
            return true;
          }
        }
        return false;
      }

      // 检查筛选器的值是否有效（排除“none”值）

    }, {
      key: 'checkFilterValue',
      value: function checkFilterValue(filterKey) {
        var filter = this.getFilterByKey(filterKey);
        var value = this.data[filterKey];

        if (!value) {
          return false;
        }
        if (_typeof(filter.none) === 'object') {
          if (filter.multiple) {
            value = value[0];
          }
          if (value.toString() === filter.none.value.toString()) {
            return false;
          }
        }
        return true;
      }

      /**
       * 检查筛选器是否可见
       * 筛选器是否可见，取决于是否有可选的选项、是否满足依赖
       * @param {Object} filter 筛选器对象
       */

    }, {
      key: 'visibleFilter',
      value: function visibleFilter(filter) {
        var i;
        var value;

        // 如果这个筛选器依赖其它筛选器
        if (filter.requires) {
          if (typeof filter.requires === 'string') {
            if (!this.checkFilterValue(filter.requires)) {
              return false;
            }
          } else if (filter.requires instanceof Array) {
            for (i = 0; i < filter.requires.length; ++i) {
              if (!this.checkFilterValue(filter.requires[i])) {
                return false;
              }
            }
          }
        }
        if (filter.type === 'daterange') {
          return !this.data[filter.key];
        }
        if (this.checkFilterItemsLoaded(filter)) {
          if (this.getFilterAvailableItems(filter).length > 0) {
            return true;
          }
          if (_typeof(filter.none) === 'object') {
            value = this.data[filter.key];
            if (!value || value.length < 1) {
              return true;
            }
            if (filter.multiple) {
              return value.indexOf(filter.none.value.toString()) === -1;
            }
            return value !== filter.none.value.toString();
          }
          return false;
        }
        return true;
      }
    }, {
      key: 'isEmpty',
      value: function isEmpty() {
        var that = this;
        var emptyFilters = that.filters.every(function (filter) {
          var value = that.data[filter.key];
          return typeof value === 'undefined' || value.length < 1;
        });
        return emptyFilters && !this.data.search && !this.input;
      }

      // 更新输入框内的占位符

    }, {
      key: 'updatePlaceholder',
      value: function updatePlaceholder() {
        if (!this.isEmpty() || this.target) {
          this.$input.attr('placeholder', '');
        } else {
          this.$input.attr('placeholder', this.text.placeholder);
        }
      }

      // 更新“清除”按钮的状态

    }, {
      key: 'updateClearButton',
      value: function updateClearButton() {
        if (!this.isEmpty() || this.target || this.$input.val()) {
          this.$btnClearSearch.removeClass('hidden');
        } else {
          this.$btnClearSearch.addClass('hidden');
        }
      }
    }, {
      key: 'focus',
      value: function focus() {
        this.$input.focus();
      }
    }, {
      key: 'hasFocus',
      value: function hasFocus() {
        if (this.active || this.$input.is(':focus')) {
          return true;
        }
        return false;
      }

      // 锁定显示一段时间，避免下拉框自动隐藏

    }, {
      key: 'lockShow',
      value: function lockShow() {
        var _this2 = this;

        this.active = true;
        // 功能与 deferFocusTime() 互斥，所以取消它的定时器
        if (this.timerForFocus) {
          clearTimeout(this.timerForFocus);
        }
        this.timerForFocus = setTimeout(function () {
          _this2.active = false;
        }, 300);
      }

      // 延长焦点状态时间，避免在做一些操作时因取消焦点而导致下拉框自动隐藏

    }, {
      key: 'deferFocusTime',
      value: function deferFocusTime() {
        var _this3 = this;

        if (this.active && this.timerForFocus) {
          return;
        }
        if (this.timerForFocus) {
          clearTimeout(this.timerForFocus);
        }
        this.active = true;
        this.timerForFocus = setTimeout(function () {
          _this3.active = false;
          // 如果输入框还未获得焦点，则隐藏下拉框
          if (!_this3.$input.is(':focus')) {
            _this3.$inputDropdown.dropdown('hide');
          }
        }, 200);
      }
    }, {
      key: 'resetInputPosition',
      value: function resetInputPosition() {
        var _this4 = this;

        // 如果已经在末尾，就不重复变动了，避免下拉框重复收起和展开
        if (this.$inputDropdown.is(':last-child')) {
          return;
        }
        setTimeout(function () {
          // 移动输入框到筛选器列表的末尾
          _this4.$tokens.append(_this4.$inputDropdown);
        }, 400);
      }
    }, {
      key: 'addHistory',
      value: function addHistory(data) {
        this.history.add(this.filters, data);
        this.buildInputDropdown();
      }
    }, {
      key: 'submit',
      value: function submit() {
        var that = this;
        var data = {};

        that.filters.forEach(function (f) {
          var value = that.data[f.key];
          if (typeof value !== 'undefined') {
            data[f.key] = that.data[f.key];
          }
        });
        that.applyInput();
        data.search = that.data.search;
        that.callback(cleanCopy(data));
      }
    }, {
      key: 'updateState',
      value: function updateState() {
        this.updatePlaceholder();
        this.updateClearButton();
      }

      // 提交当前的改动

    }, {
      key: 'commit',
      value: function commit() {
        this.updateState();
        this.$el.trigger('change.fsb', cleanCopy(this.data));
      }

      // 清除全部筛选器

    }, {
      key: 'clear',
      value: function clear() {
        this.$el.find('.js-visual-token').remove();
        this.data = {};
        this.input = '';
        this.$input.val('');
        this.clearTargetFilter();
      }

      // 设置筛选器列表

    }, {
      key: 'setFilters',
      value: function setFilters(filters) {
        var that = this;
        var keys = filters.map(function (f) {
          return f.key;
        });
        that.filters.forEach(function (f) {
          if (keys.indexOf(f.key) < 0) {
            that.removeFilter(f.key);
          }
        });
        that.clearTargetFilter();
        that.filters = filters;
        that.filters.forEach(function (f) {
          // 如果该筛选器使用的是远程的选项列表，且之前已经有缓存选项列表，则恢复它
          if (_typeof(f.remote) === 'object' && that.checkFilterItemsLoaded(f)) {
            that.loadFilterRemoteItems(f);
          }
        });
        that.buildInputDropdown();
      }

      /**
       * 设置选中的筛选条件
       * @param {Object} data 筛选条件数据
       */

    }, {
      key: 'setData',
      value: function setData(data) {
        this.clear();
        this.data = data;
        this.buildFilterTokens();
        this.buildInputDropdown();
        this.buildSearchTerm();
        this.updateState();
      }

      // 销毁组件

    }, {
      key: 'destroy',
      value: function destroy() {
        this.clear();
        this.$el.off('click');
        this.removeDateRangePicker();
        unbindEventHandlers();
      }
    }]);
    return FilteredSearchBox;
  }(Builder);

  /* eslint-disable no-param-reassign */

  $.fn.filteredSearchBox = function (options) {
    var that = this.data('filteredsearchbox');
    if (that) {
      that.destroy();
    }
    this.data('filteredsearchbox', new FilteredSearchBox(this, options));
    return this;
  };

  var defaults$2 = {
    readonly: false,
    key: 'state',
    name: 'board',
    message: {
      loading: 'loading ...',
      stateDisabled: 'Current issue cannot switch to this state',
      allComplete: 'Showing all issues',
      btnSetFirst: 'Move it to the first',
      btnSetLast: 'Move it to the last'
    },
    className: {
      iconComment: 'icon comments outline',
      iconAngleLeft: 'icon angle double left',
      iconAngleRight: 'icon angle double right',
      iconIssue: 'icon sticky note outline',
      card: 'ui link card',
      action: 'ui button',
      actions: 'ui mini basic icon buttons',
      avatar: 'ui avatar image'
    },
    actions: function actions(config) {
      if (!config.plugins.Sortable) {
        return [];
      }
      return [{
        id: 'btn-set-first',
        class: config.className.action,
        icon: config.className.iconAngleLeft,
        title: config.message.btnSetFirst,
        callback: function callback(boards, board) {
          var state = board.state.toString();
          var states = boards.sortable.toArray();
          var i = states.indexOf(state);

          if (i >= 0) {
            states.splice(i, 1);
            states.splice(0, 0, state);
            boards.sortable.sort(states);
            config.onSorted(states);
            boards.load();
          }
        }
      }, {
        id: 'btn-set-last',
        class: config.className.action,
        icon: config.className.iconAngleRight,
        title: config.message.btnSetLast,
        callback: function callback(boards, board) {
          var state = board.state.toString();
          var states = boards.sortable.toArray();
          var i = states.indexOf(state);

          if (i >= 0) {
            states.splice(i, 1);
            states.push(state);
            boards.sortable.sort(states);
            config.onSorted(states);
            boards.load();
          }
        }
      }];
    },

    data: [{
      order: 1,
      name: 'Backlog',
      state: 'open',
      color: '#ffa726',
      issuesCount: 0
    }, {
      order: 2,
      name: 'Done',
      state: 'closed',
      color: '#2baf2b',
      issuesCount: 0
    }],
    plugins: {},
    types: [],
    user: {
      id: 0,
      admin: false
    },

    /**
     * 在任务列表加载完后的回调
     * @callback BoardLoadCallback
     * @param {Array} issues 任务列表
     * @param {Number} count 任务总数
     */

    /**
     * 在开始加载任务列表时的回调
     * @param {Object,Board} board 板块对象
     * @param {BoardLoadCallback} callback 用于接收任务列表的回调函数
     */
    onLoad: function onLoad(board, callback) {
      $.ajax({
        url: 'issues',
        data: {
          state: board.state,
          page: board.page
        }
      }).done(function () {
        callback([], 0);
      }).fail(function () {
        callback([], 0);
      });
    },

    /**
     * 在更新任务时的回调
     * @param {Object} issue 任务
     * @param {String,Number} oldState 更新前的状态
     */
    onUpdate: function onUpdate(issue, oldState) {
      var _this = this;

      $.ajax({
        type: 'PUT',
        url: issue.url,
        data: {
          state: issue.state
        }
      }).done(function (res) {
        _this.updateIssue(res);
      }).fail(function () {
        _this.setIssueState(issue.id, oldState);
      });
    },

    /**
     * 在渲染任务卡片时的回调
     * @param {Object} issue 任务
     * @param {JQuery} $el 任务卡片
     */
    onRender: function onRender(issue, $el) {
      $el.addClass('issue-state-' + issue.state);
      return $el;
    },

    /**
     * 在板块被排序后的回调
     * @param {Array} states 状态列表
     */
    onSorted: function onSorted(states) {
      window.console.log(states);
    }
  };

  var Config$1 = function Config(config) {
    classCallCheck(this, Config);

    $.extend(this, defaults$2, config);
    if (config.className) {
      this.className = $.extend({}, defaults$2.className, config.className);
    }
    if (config.message) {
      this.message = $.extend({}, defaults$2.message, config.message);
    }
  };

  var htmlSafe = function () {
    var $el = $('<div/>');

    return function (text) {
      return $el.text(text).html();
    };
  }();

  var Renderer$1 = function () {
    function Renderer(config) {
      classCallCheck(this, Renderer);

      this.config = config;
    }

    createClass(Renderer, [{
      key: 'getAvatarUrl',
      value: function getAvatarUrl(name, avatarUrl) {
        var Avatar = this.config.plugins.LetterAvatar;
        if (!avatarUrl || avatarUrl.indexOf('no_portrait.png') === 0) {
          if (Avatar) {
            return Avatar(name);
          }
        }
        return avatarUrl;
      }
    }, {
      key: 'getCardId',
      value: function getCardId(issue) {
        if (this.config.getIusseId) {
          return this.config.getIusseId(issue);
        }
        return 'issue-card-' + issue.id;
      }
    }]);
    return Renderer;
  }();

  /* eslint-disable indent */

  function getUserUrl(user) {
    return user.html_url || user.path;
  }

  function renderCardUserLabel(issue) {
    if (!issue.author) {
      return '';
    }
    if (typeof issue.author.is_member !== 'undefined') {
      if (!issue.author.is_member) {
        return '<span class="user-label blue">[访客]</span>';
      }
    }
    if (issue.author.outsourced) {
      return '<span class="user-label red">[外包]</span>';
    }
    return '';
  }

  function renderCardLabels(issue) {
    var html = '';

    if (!issue.labels) {
      return '';
    }

    issue.labels.forEach(function (label) {
      html += ['<span class="label" style="background-color: #', label.color, '; color: #fff">', htmlSafe(label.name), '</span>'].join('');
    });
    return '<div class="labels">' + html + '</div>';
  }

  var CardRenderer = function (_Renderer) {
    inherits(CardRenderer, _Renderer);

    function CardRenderer() {
      classCallCheck(this, CardRenderer);
      return possibleConstructorReturn(this, (CardRenderer.__proto__ || Object.getPrototypeOf(CardRenderer)).apply(this, arguments));
    }

    createClass(CardRenderer, [{
      key: 'render',
      value: function render(issue) {
        var user = this.config.user;
        var readonly = this.config.readonly;

        var draggable = '';
        var cardClass = this.config.className.card;

        if (!readonly && (user.admin || user.id === issue.author.id)) {
          draggable = 'draggable="true"';
          cardClass += ' card-draggable';
        }
        return ['<li class="', cardClass, '" ', draggable, 'id="', this.config.name, '-issue-', issue.id, '" data-id="', issue.id, '">', '<div class="content">', '<a class="ui small header card-header" href="', issue.html_url, '" title="', htmlSafe(issue.title), '" target="_blank">', htmlSafe(issue.title), '</a>', '</div>', '<div class="extra content">', this.renderAssignee(issue), this.renderState(issue), '<span class="card-number">#', issue.number, '</span>', 'by <a class="card-author" href="', getUserUrl(issue.author), '" target="_blank">', htmlSafe(issue.author.name), '</a>', renderCardUserLabel(issue), this.renderComments(issue), renderCardLabels(issue), '</div>', '</li>'].join('');
      }
    }, {
      key: 'renderState',
      value: function renderState(issue) {
        var state = issue.state_data;

        if (!state || this.config.key === 'state') {
          return '';
        }
        return ['<div class="state">', '<i title="', state.name, '" class="', state.icon, '"', 'style="color: ', state.color, '"></i>', '</div>'].join('');
      }
    }, {
      key: 'renderAssignee',
      value: function renderAssignee(issue) {
        var user = issue.assignee;

        if (!user || !user.id || this.config.key !== 'state') {
          return '';
        }
        return ['<div class="assignee">', '<a target="_blank" href="', getUserUrl(user), '" title="', htmlSafe(user.name), '">', '<img src="', this.getAvatarUrl(user.name, user.avatar_url), '" alt="', htmlSafe(user.name), '" class="', this.config.className.avatar, '">', '</a>', '</div>'].join('');
      }
    }, {
      key: 'renderComments',
      value: function renderComments(issue) {
        var className = this.config.className.iconComment;

        if (!issue.comments) {
          return '';
        }
        return '<span class="card-comments"><i class="' + className + '"></i>' + (issue.comments + '</span>');
      }
    }]);
    return CardRenderer;
  }(Renderer$1);

  /* eslint-disable no-plusplus */

  function setColorAlpha(color, alpha) {
    var reg = /^#([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})$/;
    var matches = reg.exec(color);
    var r = parseInt(matches[1], 16);
    var g = parseInt(matches[2], 16);
    var b = parseInt(matches[3], 16);
    return 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')';
  }

  function renderBoardIcon(board) {
    var iconStyle = '';

    if (board.color) {
      iconStyle = 'style="color: ' + board.color + '"';
    }
    if (board.icon) {
      return '<i class="iconfont ' + board.icon + '" ' + iconStyle + '></i>';
    }
    return '';
  }

  var BoardRenderer = function (_Renderer) {
    inherits(BoardRenderer, _Renderer);

    function BoardRenderer() {
      classCallCheck(this, BoardRenderer);
      return possibleConstructorReturn(this, (BoardRenderer.__proto__ || Object.getPrototypeOf(BoardRenderer)).apply(this, arguments));
    }

    createClass(BoardRenderer, [{
      key: 'render',
      value: function render(board) {
        return ['<div class="board" data-state="', board.state, '">', '<div class="board-inner">', this.renderHeader(board), '<div class="board-list-wrapper">', '<ul class="board-list"></ul>', '<div class="ui inverted dimmer">', '<div class="ui loader"></div>', '</div>', '<div class="board-blur-message">', '<p><i class="icon ban"></i></p>', '<p>', this.config.message.stateDisabled, '</p>', '</div>', '</div>', '</div>', '</div>'].join('');
      }
    }, {
      key: 'renderHeader',
      value: function renderHeader(board) {
        var headerStyle = '';
        var headerClass = 'board-header';

        if (board.color) {
          if (board.colorTarget === 'background') {
            headerStyle = 'background-color: ' + setColorAlpha(board.color, 0.04);
          } else {
            headerClass += ' has-border';
            headerStyle = 'border-color: ' + board.color;
          }
        }
        return ['<div class="', headerClass, '" style="', headerStyle, '">', '<h3 class="board-title">', this.renderActions(), '<div class="right floated issues-count-badge">', '<i class="', this.config.className.iconIssue, '" />', '<span class="issues-count">', board.issuesCount, '</span>', '</div>', this.renderAvatar(board), renderBoardIcon(board), htmlSafe(board.name), '</h3>', '</div>'].join('');
      }
    }, {
      key: 'renderActions',
      value: function renderActions() {
        var config = this.config;
        var actions = config.actions;

        if (typeof actions === 'function') {
          actions = actions(config);
        }
        return ['<div class="right floated board-actions ', config.className.actions, '">', actions.map(function (a) {
          return ['<button type="button" class="board-action ', a.class, '" title="', a.title, '" data-id="', a.id, '">', '<i class="', a.icon, '" />', '</button>'].join('');
        }).join(''), '</div>'].join('');
      }
    }, {
      key: 'renderAvatar',
      value: function renderAvatar(board) {
        if (!board.avatarUrl) {
          return '';
        }
        return ['<img class="', this.config.className.avatar, ' board-avatar" alt="', board.state, '" src="', this.getAvatarUrl(board.name, board.avatarUrl), '">'].join('');
      }
    }, {
      key: 'renderTip',
      value: function renderTip(board) {
        if (board.loadable) {
          return ['<li class="board-tip">', '<span class="ui active mini inline loader" /> ', this.config.message.loading, '</li>'].join('');
        }
        if (board.completed && board.issues.length > 0) {
          return ['<li class="board-tip">', this.config.message.allComplete, '</li>'].join('');
        }
        return '<li class="board-tip"></li>';
      }
    }]);
    return BoardRenderer;
  }(Renderer$1);

  var Board = function () {
    function Board(data, config) {
      var _this2 = this;

      classCallCheck(this, Board);

      this.page = 0;
      this.loadable = true;
      this.loading = false;
      this.completed = false;
      this.issues = [];

      this.name = data.name;
      this.icon = data.icon;
      this.state = data.state;
      this.color = data.color;
      this.colorTarget = data.colorTarget;
      this.issuesCount = data.issuesCount || 0;
      this.avatarUrl = data.avatarUrl;

      this.config = config;

      this.renderer = new BoardRenderer(config);
      this.cardRenderer = new CardRenderer(config);

      this.$tip = null;
      this.$el = $(this.renderer.render(this));
      this.$dimmer = this.$el.find('.ui.dimmer');
      this.$issues = this.$el.find('.board-list');
      this.$issuesCount = this.$el.find('.issues-count');

      this.$issues.on('scroll', function () {
        _this2.autoload();
      });
    }

    createClass(Board, [{
      key: 'add',
      value: function add(issue) {
        this.issues.push(issue);
        this.issuesCount += 1;
        this.$issuesCount.text(this.issuesCount);
      }
    }, {
      key: 'find',
      value: function find(id) {
        for (var i = 0; i < this.issues.length; ++i) {
          var issue = this.issues[i];
          if (issue.id === id) {
            return i;
          }
        }
        return -1;
      }
    }, {
      key: 'get',
      value: function get$$1(id) {
        var i = this.find(id);

        if (i >= 0) {
          return this.issues[i];
        }
        return null;
      }
    }, {
      key: 'remove',
      value: function remove(id) {
        var i = this.find(id);

        if (i >= 0) {
          this.issuesCount -= 1;
          this.$issuesCount.text(this.issuesCount);
          return this.issues.splice(i, 1);
        }
        return null;
      }
    }, {
      key: 'clear',
      value: function clear() {
        this.page = 0;
        this.issues = [];
        this.issuesCount = 0;
        this.loading = false;
        this.loadable = true;
        this.completed = false;

        this.$issuesCount.text(0);
        this.$issues.empty();
        this.$tip = null;
      }
    }, {
      key: 'autoload',
      value: function autoload() {
        if (this.completed) {
          return;
        }
        if (!this.$tip || this.$tip.position().top < this.$issues.height()) {
          this.load();
        }
      }
    }, {
      key: 'load',
      value: function load() {
        if (this.loading) {
          return false;
        }
        this.page += 1;
        this.loading = true;
        if (this.page === 1) {
          this.$dimmer.addClass('active');
        }
        this.config.onLoad(this, this.onLoadDone.bind(this));
        return true;
      }
    }, {
      key: 'onLoadDone',
      value: function onLoadDone(issues, count) {
        this.issuesCount = count;
        this.$issuesCount.text(this.issuesCount);
        this.appendIssues(issues);
        if (this.issuesCount > 0) {
          if (issues.length < 1 || this.issuesCount === issues.length) {
            this.loadable = false;
            this.completed = true;
          } else {
            this.loadable = true;
            this.completed = false;
          }
        } else {
          this.loadable = false;
          this.completed = true;
        }
        if (this.page === 1) {
          this.$dimmer.removeClass('active');
        }
        this.loading = false;
        this.updateTip();
        this.autoload();
      }

      /* 进行初次加载 */

    }, {
      key: 'firstload',
      value: function firstload() {
        if (this.page > 0) {
          return false;
        }
        return this.load();
      }

      /**
       * 更新提示
      */

    }, {
      key: 'updateTip',
      value: function updateTip() {
        if (this.$tip) {
          this.$tip.remove();
        }
        this.$tip = $(this.renderer.renderTip(this));
        this.$issues.append(this.$tip);
      }
    }, {
      key: 'createCard',
      value: function createCard(issue) {
        var $card = $(this.cardRenderer.render(issue));
        var onSelect = this.config.onSelect;

        if (onSelect) {
          $card.on('click', function (e) {
            var $target = $(e.target);

            if (!$target.is('a')) {
              $target = $target.parent();
            }
            if (!$target.parent().hasClass('card-header') && $target.is('a') && $target.attr('href')) {
              return;
            }
            onSelect(issue, e);
          });
        }
        return this.config.onRender(issue, $card);
      }
    }, {
      key: 'appendIssue',
      value: function appendIssue(issue) {
        this.issues.push(issue);
        this.$issues.append(this.createCard(issue));
      }
    }, {
      key: 'prependIssue',
      value: function prependIssue(issue) {
        this.issuesCount += 1;
        this.issues.splice(0, 0, issue);
        this.$issues.prepend(this.createCard(issue));
        this.$issuesCount.text(this.issuesCount);
      }
    }, {
      key: 'updateIssue',
      value: function updateIssue(issue) {
        var $issue = $('#' + this.config.name + '-issue-' + issue.id);

        if ($issue.length < 1) {
          this.prependIssue(issue);
          return;
        }
        $issue.before(this.createCard(issue)).remove();
      }

      /**
       * 直接追加多个任务，不更新任务总数
       * @param {Array} issues 任务列表
       */

    }, {
      key: 'appendIssues',
      value: function appendIssues(issues) {
        var _this3 = this;

        issues.filter(function (issue) {
          return _this3.issues.every(function (boardIssue) {
            return boardIssue.id !== issue.id;
          });
        }).forEach(function (issue) {
          issue[_this3.config.Key] = _this3.state;
          _this3.appendIssue(issue);
        });
      }
    }]);
    return Board;
  }();

  var Boards = function () {
    /**
     * 创建一个看板
     * @param {JQuery} $el 看板容器元素
     * @param {BoardsSettings} options 配置
     */
    function Boards($el, options) {
      classCallCheck(this, Boards);

      var config = new Config$1(options);

      this.$el = $el;
      this.config = config;

      this.boards = {};
      this.$hoverIssue = null;
      this.$selectedIssue = null;
      this.timerForScrollLoad = null;

      this.bindDrag();
      this.bindScroll();
      this.initPlugins();
      this.setData(config.data);
    }

    createClass(Boards, [{
      key: 'initPlugins',
      value: function initPlugins() {
        this.initSortable();
      }

      // 初始化拖拽排序功能

    }, {
      key: 'initSortable',
      value: function initSortable() {
        var that = this;
        var Sortable = that.config.plugins.Sortable;

        // 如果没有注入 SortableJS 依赖，则不启用这个功能
        if (!Sortable) {
          return false;
        }
        that.$el.addClass('boards-sortable');
        that.sortable = Sortable.create(that.$el[0], {
          handle: '.board-title',
          dataIdAttr: 'data-state',
          onUpdate: function onUpdate() {
            that.config.onSorted(that.sortable.toArray());
          }
        });
        that.$el.on('click', '.board-action', function (e) {
          var $target = $(this);
          var id = $target.data('id');
          var state = $target.parents('.board').data('state');
          var actions = that.config.actions;

          if (typeof actions === 'function') {
            actions = actions(that.config);
          }
          actions.some(function (action) {
            if (action.id === id && action.callback) {
              action.callback(that, that.boards[state], e);
              return true;
            }
            return false;
          });
        });
        return true;
      }
    }, {
      key: 'bindScroll',
      value: function bindScroll() {
        var that = this;
        var timerForScrollLoad = null;

        function onScrollLoad() {
          if (timerForScrollLoad) {
            clearTimeout(timerForScrollLoad);
          }
          timerForScrollLoad = setTimeout(function () {
            timerForScrollLoad = null;
            that.load();
          }, 200);
        }

        $(window).on('resize', onScrollLoad);
        this.$el.on('scroll', onScrollLoad);
      }
    }, {
      key: 'bindDrag',
      value: function bindDrag() {
        var that = this;

        if (that.config.readonly) {
          return;
        }
        that.$el.on('dragstart', '.card', function (e) {
          e.stopPropagation();
          that.$selectedIssue = $(this);
          var issueId = $(this).data('id');
          var issue = that.getIssue(issueId);
          if (issue) {
            that.setFocus(issue.type, issue.state);
          }
        });
        that.$el.on('dragend', '.card', function () {
          that.clearFocus();
        });
        that.$el.on('drop', '.board-list', function (e) {
          e.preventDefault();
          e.stopPropagation();

          if (that.$hoverIssue) {
            that.$hoverIssue.removeClass('card-dragover');
          }
          if (!that.$selectedIssue) {
            return false;
          }
          if (that.config.readonly) {
            return false;
          }

          var $issue = that.$selectedIssue;
          var issueId = $issue.data('id');
          var $board = $(this).parents('.board');
          var state = $board.data('state');
          var oldState = $issue.parents('.board').data('state');
          var nextIssueId = that.$hoverIssue ? that.$hoverIssue.data('id') : null;

          that.setIssueState(issueId, state, nextIssueId, function (issue) {
            that.config.onUpdate(issue, oldState, nextIssueId);
          });
          return true;
        });
        that.$el.on('dragover', '.board-list', function (e) {
          var key = that.config.key;
          var $target = $(e.target);

          if (!that.$selectedIssue) {
            return;
          }

          e.preventDefault();
          while ($target.length > 0 && !$target.hasClass('card')) {
            $target = $target.parent();
          }
          if ($target.length < 1) {
            if (that.$hoverIssue) {
              that.$hoverIssue.removeClass('card-dragover');
            }
            that.$hoverIssue = null;
            return;
          }
          if (that.$hoverIssue) {
            that.$hoverIssue.removeClass('card-dragover');
          }
          that.$hoverIssue = $target;
          var hoverIssue = that.getIssue($target.data('id'));
          var selectedIssue = that.getIssue(that.$selectedIssue.data('id'));
          if (hoverIssue && selectedIssue && hoverIssue[key] !== selectedIssue[key]) {
            that.$hoverIssue.addClass('card-dragover');
          }
        });
      }

      /**
       * 设置焦点板块
       * 根据指定的类型和状态，排除无关状态的板块
       * @param {String, Number} typeId 任务类型 id
       * @param {String, Number} stateId 任务状态 id
       */

    }, {
      key: 'setFocus',
      value: function setFocus(typeId, stateId) {
        var _this = this;

        var stateIds = [];
        var issueType = null;

        if (this.config.key !== 'state') {
          return;
        }
        this.config.types.some(function (t) {
          if (t.id === typeId) {
            issueType = t;
            return true;
          }
          return false;
        });
        if (issueType) {
          stateIds = issueType.states.map(function (state) {
            return state.id.toString();
          });
        }
        Object.keys(this.boards).forEach(function (id) {
          if (id !== stateId.toString() && stateIds.indexOf(id) === -1) {
            _this.boards[id].$el.addClass('board-blur');
          }
        });
      }

      /* 清除板块的焦点效果 */

    }, {
      key: 'clearFocus',
      value: function clearFocus() {
        var _this2 = this;

        Object.keys(this.boards).forEach(function (key) {
          _this2.boards[key].$el.removeClass('board-blur');
        });
      }
    }, {
      key: 'getIssueCard',
      value: function getIssueCard(id) {
        return $('#' + this.config.name + '-issue-' + id);
      }
    }, {
      key: 'getIssue',
      value: function getIssue(id) {
        var $issue = this.getIssueCard(id);
        var $board = $issue.parents('.board');
        var state = $board.data('state');
        var board$$1 = this.boards[state];
        if (board$$1) {
          return board$$1.get(id);
        }
        return null;
      }
    }, {
      key: 'removeIssue',
      value: function removeIssue(id) {
        var issue = null;
        var $issue = this.getIssueCard(id);

        if ($issue.length < 1) {
          return issue;
        }

        var state = $issue.parents('.board').data('state');
        if (state) {
          var board$$1 = this.boards[state];
          if (board$$1) {
            issue = board$$1.remove(id);
          }
        }

        $issue.remove();
        return issue;
      }
    }, {
      key: 'updateIssue',
      value: function updateIssue(issue) {
        var board$$1 = this.boards[issue.state];
        if (board$$1) {
          board$$1.updateIssue(issue);
          return true;
        }
        return false;
      }
    }, {
      key: 'prependIssue',
      value: function prependIssue(issue) {
        var board$$1 = this.boards[issue.state];
        if (board$$1) {
          board$$1.prependIssue(issue);
          return true;
        }
        return false;
      }
    }, {
      key: 'setIssueState',
      value: function setIssueState(issueId, state, nextIssueId, callback) {
        var $issue = this.getIssueCard(issueId);
        var $nextIssue = nextIssueId ? this.getIssueCard(nextIssueId) : null;

        if ($issue.length < 1) {
          return null;
        }

        var user = this.config.user;
        var $oldBoard = $issue.parents('.board');
        var oldState = $oldBoard.data('state');
        var oldBoard = this.boards[oldState];
        var newBoard = this.boards[state];

        if (oldBoard.state === state) {
          return null;
        }

        // 如果新的板块不接受该状态的 issue
        if (newBoard.exclude && newBoard.exclude.indexOf(oldState) >= 0) {
          return null;
        }

        var issue = oldBoard.get(issueId);
        // 如果当前用户既不具备管理权限，也不是 issue 作者，则禁止操作
        if (!user.admin && issue.author.id !== user.id) {
          return null;
        }

        issue[this.config.key] = state;
        newBoard.add(issue);
        oldBoard.remove(issue.id);
        $issue.hide(256, function () {
          // 如果有指定下一个 issue，则将当前 issue 插入到它前面
          if ($nextIssue) {
            $nextIssue.before($issue);
          } else {
            newBoard.$issues.prepend($issue);
          }
          $issue.show(256, function () {
            if (callback) {
              callback(issue);
            }
          });
        });
        oldBoard.autoload();

        return issue;
      }
    }, {
      key: 'load',
      value: function load() {
        var _this3 = this;

        var count = 0;
        var bound = this.$el.offset();

        // 设置边界框（可见区域）的尺寸
        bound.width = this.$el.width();
        bound.height = this.$el.height();

        Object.keys(this.boards).forEach(function (state) {
          var board$$1 = _this3.boards[state];
          var offset = board$$1.$el.offset();
          // 如果当前板块在可见区域内
          if (offset.top + board$$1.$el.height() > bound.top && offset.left + board$$1.$el.width() > bound.left && offset.top < bound.top + bound.height && offset.left < bound.left + bound.width) {
            if (board$$1.firstload()) {
              count += 1;
            }
          }
        });
        return count;
      }
    }, {
      key: 'setData',
      value: function setData(data) {
        var _this4 = this;

        this.boards = {};
        this.$el.addClass('boards-list').empty();

        data.forEach(function (boardData) {
          var board$$1 = new Board(boardData, _this4.config);
          _this4.boards[boardData.state] = board$$1;
          _this4.$el.append(board$$1.$el);
        });
      }
    }, {
      key: 'clearData',
      value: function clearData() {
        var _this5 = this;

        Object.keys(this.boards).forEach(function (state) {
          _this5.boards[state].clear();
        });
      }
    }]);
    return Boards;
  }();

  $.fn.boards = function (options) {
    var that = this.data('boards');
    var settings = $.extend({}, $.fn.boards.settings, options);

    if (!that) {
      that = new Boards(this, settings);
      this.data('boards', that);
      that.load();
    }
    return this;
  };

  $.fn.boards.settings = defaults$2;

}());
